package com.wanji.single_point.service.impl;

import com.wanji.single_point.entity.Radar;
import com.wanji.single_point.pojo.Parameter;
import com.wanji.single_point.pojo.PutParam;
import com.wanji.single_point.pojo.RadarResult;
import com.wanji.single_point.result.PartpPerceiveResult;
import com.wanji.single_point.service.RadarService;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author xinyingjie
 * @since 2022-07-14
 */
@Service
public class RadarServiceImpl  implements RadarService {


    private static final double match_time = 0.05;


    private static Map<String, PartpPerceiveResult> resultMap = new ConcurrentHashMap<>();
    private static  RadarResult radarResult = new RadarResult();




    @Override
    public void putData(String userId, Radar[] radarReal, Radar[] radarTest,PutParam param) {
        PartpPerceiveResult result = null;
        if (!resultMap.containsKey(userId)){
            result = new PartpPerceiveResult();
            result.setCountFrame(0);
        }else {
            result = resultMap.get(userId);
            result.setCountFrame(result.getCountFrame()+1);
        }

        result = paticpPerceive(userId,param,radarReal, radarTest, result, result.getCountFrame());
//        System.out.println("最终"+result.getTemp_real_dic());
        resultMap.put(userId,result);

    }

    @Override
    public RadarResult getResult(String userId) {
        PartpPerceiveResult result = resultMap.get(userId);
        System.out.println(result);
        if (result != null){
            radarResult.setNowRealNum(result.getNow_real_num());
            radarResult.setNowTestNum(result.getNow_test_num());
            radarResult.setTotalRealNum(result.getTotal_real_num());
            radarResult.setTotalTestNum(result.getTotal_test_num());
            radarResult.setTotalRate(result.getTotal_rate());
            if (result.getParameter() != null){
                radarResult.setParameter(result.getParameter());
            }else {
                System.out.println("Parameter为空");
                System.out.println(result);
                Parameter parameter = new Parameter();
                radarResult.setParameter(parameter);
            }
            Map<String, Map<Integer, Radar>> match_plate_dic = result.getMatch_plate_dic();
            for (String key : match_plate_dic.keySet()) {
                Map<Integer, Radar> radarMap = match_plate_dic.get(key);
                radarResult.setRadarMap(radarMap);
                break;
            }
            return radarResult;
        }else {
            PartpPerceiveResult result1 = new PartpPerceiveResult();
            radarResult.setNowRealNum(result1.getNow_real_num());
            radarResult.setNowTestNum(result1.getNow_test_num());
            radarResult.setTotalRealNum(result1.getTotal_real_num());
            radarResult.setTotalTestNum(result1.getTotal_test_num());
            radarResult.setTotalRate(result1.getTotal_rate());
            Parameter parameter = new Parameter();
            radarResult.setParameter(parameter);

            return radarResult;
        }


    }

    @Override
    public void deleteCache(String userId) {
        resultMap.remove(userId);
    }

    @Override
    public PutParam putParamer(String realCode, String testCode, Long beginTime, Long endTime) {
        PutParam param = new PutParam();
        param.setRealCode(realCode);
        param.setTestCode(testCode);
        param.setBeginTime(beginTime);
        param.setEndTime(endTime);
        return param;
    }


    /**
     * 整体计算逻辑，通过判断真值和测试系统的数据有无进行相应的逻辑处理
     *
     * @param radars_real
     * @param radars_test
     * @param result
     * @param count_frame
     * @return
     */
    private PartpPerceiveResult paticpPerceive(String userId, PutParam param,Radar[] radars_real, Radar[] radars_test, PartpPerceiveResult result, int count_frame) {

        radars_real = saveRadar(radars_real,param,0);
        radars_test = saveRadar(radars_test,param,1);
        if (radars_real == null && radars_test == null){
            deleteCache(userId);
            return result;
        }
        //  如果真值系统当前帧有数据， 更新真值系统的瞬时和累计结果
        if (null != radars_real) {
            // 如果被测设备当前帧有数据
            if (null != radars_test) {
                int time_match_flag = getTimeMatchFlag(radars_real, radars_test, match_time);
                if (time_match_flag == 1) {
                    result = update(count_frame, result, radars_real, radars_test);
                } else {

                    result = AddCacheTest(count_frame, result, radars_real);
                    result = AddCacheReal(count_frame, result, radars_test);

                }
            } else {
                result = AddCacheTest(count_frame, result, radars_real);
            }
        } else {
            if (null != radars_test) {
                result = AddCacheReal(count_frame, result, radars_test);
            }
        }


        result = ChangeRate(result);
        result = showInfo(result, count_frame);
        return result;
    }

    private Radar[] saveRadar(Radar[] radars_real,PutParam param,int flag) {
        List<Radar> list = new ArrayList<>();
        if (radars_real == null){
            return null;
        }
        for (Radar radar : radars_real) {
            long realCreateTime = radar.getCreateTime();
            if (flag == 0){
                if (StringUtils.equals(radar.getIotDeviceCode(), param.getRealCode()) &&realCreateTime >= param.getBeginTime() &&
                        realCreateTime <= param.getEndTime()){
                    list.add(radar);
                }
            }
            if (flag == 1){
                if (StringUtils.equals(radar.getIotDeviceCode(), param.getTestCode()) &&realCreateTime >= param.getBeginTime() &&
                        realCreateTime <= param.getEndTime()){
                    list.add(radar);
                }
            }

        }
        if (list.size()>0){
            Radar[] radars = new Radar[list.size()];
            for (int i = 0; i < radars.length; i++) {
                radars[i] = list.get(i);
            }
            return radars;
        }else {
            return null;
        }


    }


    private PartpPerceiveResult showInfo(PartpPerceiveResult result, int count_frame) {
        Map<String, Map<Integer, Radar>> match_plate_dic = result.getMatch_plate_dic();
        if (!match_plate_dic.isEmpty()) {
            for (String key : match_plate_dic.keySet()) {
                Radar radar = match_plate_dic.get(key).get(0);
                if (radar.getParameter().getFrame() == count_frame) {
                    result.setParameter(radar.getParameter());
                    return result;
                }
            }
        }
        return result;

    }


    private PartpPerceiveResult ChangeRate(PartpPerceiveResult result) {
        int[] total_rate = result.getTotal_rate();
        int[] total_real_num = result.getTotal_real_num();
        int[] total_test_num = result.getTotal_test_num();
        for (int i = 0; i < 3; i++) {
            if (total_real_num[i] > 0) {
//                double v = changeSum(total_test_num[i] / total_real_num[i] * 100);

//                System.out.println(total_test_num[i] / total_real_num[i]);
                total_rate[i] = changeSum((double) total_test_num[i] / (double)total_real_num[i]) ;
            } else {
                if (total_test_num[i] == 0 && total_real_num[i] == 0){
                    total_rate[i] = 100;
                }

            }
        }
        result.setTotal_rate(total_rate);
        return result;
    }


    private PartpPerceiveResult AddCacheReal(int count_frame, PartpPerceiveResult result, Radar[] radars_test) {
        Map<Integer, Radar[]> temp_real_dic = result.getTemp_real_dic();
        Map<Integer, Radar[]> temp_test_dic = result.getTemp_test_dic();
        if (!temp_real_dic.isEmpty()) {
            List<Integer> del_real_frame = new ArrayList<>();
            for (int realKey : temp_real_dic.keySet()) {
                Radar[] radars_temp = temp_real_dic.get(realKey);
                int temp_match_time_flag = getTimeMatchFlag(radars_temp, radars_test, match_time);
                if (temp_match_time_flag == 1) {
                    del_real_frame.add(realKey);
                    result = update(count_frame, result, radars_temp, radars_test);
                    break;
                }
            }
            if (!del_real_frame.isEmpty()) {
                for (int s : del_real_frame) {
                    temp_test_dic.remove(s);
                }
            } else {
                temp_test_dic.put(count_frame, radars_test);
            }
        } else {
            temp_test_dic.put(count_frame, radars_test);
        }
        result.setTemp_real_dic(temp_real_dic);
        result.setTemp_test_dic(temp_test_dic);
        return result;
    }

    private PartpPerceiveResult AddCacheTest(int count_frame, PartpPerceiveResult result, Radar[] radars_real) {
        Map<Integer, Radar[]> temp_test_dic = result.getTemp_test_dic();
        Map<Integer, Radar[]> temp_real_dic = result.getTemp_real_dic();
        if (!temp_test_dic.isEmpty()) {
            List<Integer> del_test_frame = new ArrayList<>();
//            System.out.println(temp_real_dic);
            for (int testKey : temp_test_dic.keySet()) {
                Radar[] radars_temp = temp_test_dic.get(testKey);
                int temp_match_time_flag = getTimeMatchFlag(radars_real, radars_temp, match_time);
                if (temp_match_time_flag == 1) {
                    del_test_frame.add(testKey);
                    result = update(count_frame, result, radars_real, radars_temp);
                    break;
                }
            }
            if (!del_test_frame.isEmpty()) {
                for (int s : del_test_frame) {
                    temp_test_dic.remove(s);
                }
            } else {
                temp_real_dic.put(count_frame, radars_real);
            }
        } else {
            temp_real_dic.put(count_frame, radars_real);
        }
        result.setTemp_real_dic(temp_real_dic);
        result.setTemp_test_dic(temp_test_dic);
        return result;
    }

    /**
     * 更新数据，包括now_real_num，now_test_num，total_real_num，total_test_num
     */
    private PartpPerceiveResult update(int count_frame, PartpPerceiveResult result, Radar[] radars_real, Radar[] radars_test) {
        int real_recg_num = radars_real.length;
        int test_recg_num = radars_test.length;
        int real_clas_num = 0;
        int real_lisence_recg_num = 0;

        for (Radar radar_real : radars_real) {
            if (radar_real.getCarType() != 0) {
                real_clas_num++;
            }
            if (!radar_real.getCarPlateNo().equals("无")) {
                real_lisence_recg_num++;
            }
        }

        if (test_recg_num <= real_recg_num) {
            result = changeNum(result, real_recg_num, test_recg_num, 0);
            int[] flag1 = getTypeMatchFlag(radars_real, radars_test);
            int test_clas_num = flag1[0];
            int  type_mach_flag = flag1[1];

            if (type_mach_flag == 1) {
//                System.out.println("real_clas_num"+real_clas_num);
//                System.out.println("test_clas_num"+test_clas_num);
                result = changeNum(result, real_clas_num, test_clas_num, 1);
            }

            result = getPlateMatchFlag(count_frame, result, radars_real, radars_test);

            if (result.getMatch_plate_num() > 0) {
                if (real_lisence_recg_num < result.getMatch_plate_num()){
                    System.out.println("===================================");
                    System.out.println(Arrays.toString(radars_real));

                    System.out.println("=======================================");
                }
                result = changeNum(result, real_lisence_recg_num,
                        result.getMatch_plate_num(), 2);
            }


        }
        return result;

    }


    private PartpPerceiveResult getPlateMatchFlag(int count_frame, PartpPerceiveResult result,
                                                         Radar[] radars_real, Radar[] radars_test) {
        int match_plate_num = 0;
        List<String> temp_match_palte = new ArrayList<>();
        List<Radar> real_map_list = new ArrayList<>();
        List<Radar> test_map_list = new ArrayList<>();
        real_map_list = AddRadarNo(radars_real, real_map_list);
        test_map_list = AddRadarNo(radars_test, test_map_list);
        Map<String, Map<Integer, Radar>> match_plate_dic = result.getMatch_plate_dic();
        if (real_map_list.size() > 0 && test_map_list.size() > 0) {
            for (Radar real_map : real_map_list) {
                String real_plate = real_map.getCarPlateNo();
                for (Radar test_map : test_map_list) {
                    String test_plate = test_map.getCarPlateNo();
                    if (!(temp_match_palte.contains(real_plate) || temp_match_palte.contains(test_plate))) {
                        if (real_plate.equals(test_plate)) {
                            temp_match_palte.add(real_plate);
                            match_plate_num += 1;
                            int dis_diff = (int)CalculateDistance(real_map, test_map);
                            int speed_diff = (int)Math.abs(real_map.getSpeed() - test_map.getSpeed());
                            int corse_diff = -1;
                            if (null != real_map.getDirectionDegs() && null != test_map.getDirectionDegs()){
                                corse_diff = (int)absoluteAngle(real_map.getDirectionDegs(), test_map.getDirectionDegs());
                            }
                            if (!match_plate_dic.containsKey(real_plate)) {
                                Parameter parameter = new Parameter();
//                                System.out.println(parameter);
                                parameter.setDisDiff(dis_diff);
                                parameter.setSpeedDiff(speed_diff);
                                parameter.setCorseDiff(corse_diff);
                                real_map.setParameter(parameter);
                                Map<Integer,Radar> radarMap = new ConcurrentHashMap<>();
                                radarMap.put(0,real_map);
                                radarMap.put(1,test_map);

                                match_plate_dic.put(real_plate, radarMap);

                            }
                            result = addDic(result, real_map, real_plate, count_frame, dis_diff, speed_diff, corse_diff);
                        }
                    }

                }
            }
        }
        result.setMatch_plate_num(match_plate_num);
        result.setMatch_plate_dic(match_plate_dic);
        return result;

    }

    private PartpPerceiveResult addDic(PartpPerceiveResult result, Radar real_map, String real_plate, int count_frame,
                                              double dis_diff, double speed_diff, double corse_diff) {
        Map<String, Map<Integer, Radar>> match_plate_dic = result.getMatch_plate_dic();
        Map<Integer, Radar> radarMap = match_plate_dic.get(real_plate);
        Radar radar = radarMap.get(0);
        Parameter parameter = radar.getParameter();
        parameter.setTotal_num(parameter.getTotal_num() + 1);
        parameter.setFrame(count_frame);
        if (dis_diff <= 1) {
            parameter.setLoc_match_num(parameter.getLoc_match_num() + 1);
        }
        if (speed_diff <= 5) {
            parameter.setSpeed_match_num(parameter.getSpeed_match_num() + 1);
        }
        if (corse_diff <= 15) {
            parameter.setCorse_match_num(parameter.getCorse_match_num() + 1);
        }

        parameter.setLoc_match_rate(changeSum((double)parameter.getLoc_match_num() / (double)parameter.getTotal_num() ));
        parameter.setSpeed_match_rate(changeSum((double)parameter.getSpeed_match_num() / (double)parameter.getTotal_num()));
        parameter.setCorse_match_rate(changeSum((double)parameter.getCorse_match_num() / (double)parameter.getTotal_num()));
        real_map.setParameter(parameter);
        radarMap.put(0,real_map);
        match_plate_dic.put(real_plate, radarMap);
        result.setMatch_plate_dic(match_plate_dic);
        return result;

    }

    private int changeSum(double sum) {
        String result = String.format("%.2f",sum);
        BigDecimal bdNum = new BigDecimal(result);
//        System.out.println((int)Math.round(bdNum.doubleValue() * 100));

        return (int)Math.round(bdNum.doubleValue() * 100);
    }

    private double absoluteAngle(double angle1, double angle2) {
        double norm_deg = (angle1 - angle2) % 360;
        return Math.min(360 - norm_deg, norm_deg);
    }


    private double CalculateDistance(Radar real_map, Radar test_map) {
        double real_lng = Math.toRadians(real_map.getLng());
        double real_lat = Math.toRadians(real_map.getLat());
        double test_lng = Math.toRadians(test_map.getLng());
        double test_lat = Math.toRadians(test_map.getLat());

        double dLng = test_lng - real_lng;
        double dLat = test_lat - real_lat;

        double distance = Math.pow(Math.sin(dLat / 2), 2) +
                Math.cos(test_lat) * Math.cos(real_lat) * Math.pow(Math.sin(dLng / 2), 2);
        distance = 2 * Math.asin(Math.sqrt(distance)) * 6371.393 * 1000;
        return distance;
    }


    private int[] getTypeMatchFlag(Radar[] radars_real, Radar[] radars_test) {
        int[] result = new int[2];
        int type_match_flag = 1;
        int match_num = 0;
        result[0] = match_num;
        result[1] = type_match_flag;
        Map<Integer, Integer> test_type_dic = new HashMap<>();
        Map<Integer, Integer> real_type_dic = new HashMap<>();
        List<Radar> real_map_list = new ArrayList<>();
        List<Radar> test_map_list = new ArrayList<>();

        real_map_list = AddRadar(radars_real, real_map_list);
        test_map_list = AddRadar(radars_test, test_map_list);


        if (real_map_list.size() > 0 && test_map_list.size() > 0) {
            real_type_dic = changeDic(real_map_list, real_type_dic);
            test_type_dic = changeDic(test_map_list, test_type_dic);
            result = changeFlag(real_type_dic, test_type_dic, match_num, type_match_flag);

        }
//        System.out.println(Arrays.toString(result)+"result");
        return result;

    }

    private int[] changeFlag(Map<Integer, Integer> real_type_dic, Map<Integer, Integer> test_type_dic,
                                    int match_num, int type_match_flag) {
        int[] result = new int[2];
        for (Integer real_key : real_type_dic.keySet()) {
            if (test_type_dic.containsKey(real_key)) {
                if (real_type_dic.get(real_key) < test_type_dic.get(real_key)) {
                    type_match_flag = 0;
                    break;
                } else {
                    match_num += test_type_dic.get(real_key);
                }
            }
        }

        result[0] = match_num;
        result[1] = type_match_flag;
        return result;
    }

    private Map<Integer, Integer> changeDic(List<Radar> map_list, Map<Integer, Integer> type_dic) {
        for (Radar map : map_list) {
            int type = map.getCarType();
            if (type_dic.containsKey(type)) {
                Integer count = type_dic.get(type);
                type_dic.put(type, count + 1);
            } else {
                type_dic.put(type, 1);
            }

        }
        return type_dic;
    }

    private List<Radar> AddRadar(Radar[] radars, List<Radar> map_list) {
        for (Radar radar : radars) {
            if (radar.getCarType() != 0) {
                map_list.add(radar);
            }
        }
        return map_list;
    }


    private List<Radar> AddRadarNo(Radar[] radars, List<Radar> map_list) {
        for (Radar radar : radars) {
            if (!radar.getCarPlateNo().equals("无")) {
                map_list.add(radar);
            }
        }
        return map_list;
    }



    /**
     * 更新目标识别数
     */
    private PartpPerceiveResult changeNum(PartpPerceiveResult result, int real_num, int test_num, int index) {
        int[] now_real_num = result.getNow_real_num();
        int[] now_test_num = result.getNow_test_num();
        int[] total_real_num = result.getTotal_real_num();
        int[] total_test_num = result.getTotal_test_num();
        now_real_num[index] = real_num;
        now_test_num[index] = test_num;
        total_real_num[index] += real_num;
        total_test_num[index] += test_num;
        result.setNow_real_num(now_real_num);
        result.setNow_test_num(now_test_num);
        result.setTotal_real_num(total_real_num);
        result.setTotal_test_num(total_test_num);
        return result;
    }

    /**
     * 判断这一帧数据中测试数据和真实数据是否在设定的时间阈值之内
     */
    private int getTimeMatchFlag(Radar[] radars_real, Radar[] radars_test, double match_time) {
        int match_time_flag = 0;
        for (Radar radar_real : radars_real) {
            for (Radar radar_test : radars_test) {
                long createtime_real = radar_real.getCreateTime();
                long createtime_test = radar_test.getCreateTime();
                long time_diff = Math.abs(createtime_real - createtime_test) / 1000;
                if (time_diff < match_time) {
                    match_time_flag = 1;
                    break;
                }
            }
        }

        return match_time_flag;
    }


}
